const util = require('util');
const debug = util.debuglog('oil-game:upgrade:action');
const redis = require('redis');
const BigNumber = require("bignumber.js");
const dispatcher = require('../../../util/dispatcher');
const time = require('../../../util/time');
const utils = require('../../../util/utils');
const pushUtils = require('../../../util/pushUtils');

const {
    PushReoute,   //向客户端发送的消息路由信息
    DepthConsts,    //深度
    QualityConsts,    //品质
    SpreedConsts,   //速度
    Msg,
    PushRoute,
    MIN_LOGOUT_INTERVAL,    //最小离线时长，秒
    INCOME_SPEED_UP_SCALE,   //采集收入加速倍率
    ADD_INCOMERATE_PREFIX,    //采集收入加速
} = require('../lib/consts');
const {
    onIncomeRateAdd,    //采集收入加速开始
    onIncomeRateEnd,    //采集收入加速结束
} = require('../lib/push')
const {
    getUpgradeData,   //查看内存中User的等级信息
    setUpgradeCoins,    //设置内存中的upgrade的当前金币数量
    incrUpgradeDepth_level,   //深度升级
    reduceCoins,    //扣除金币数量
    incrUpgradeQuality_level,   //品质升级
    incrUpgradeSpreed_level,    //速度升级
    getLastLoginTime,   //获取当前用户上线时间
    getLogoutCoins,   //从缓存中获取离线金币
    saveLogoutCoins,    //存储离线金币到缓存中
    setLastUpdateCoins,   //设置内存中的上次更新时间
    setIncomeRate,    //获取是否采集收入加速
    isIncomeRate,   //获取是否采集收入加速
    doubleRewardExists, //判断双倍奖励是否开启
    openDouble, //开启双倍奖励
} = require('../lib/cache');
const {
    getLevelInfo
} = require('../lib/dao');
const {
    addCoinsApi,   //增加金币数量
    reduceCoinsApi
} = require('../lib/api');
const mapJson = require('../../../../config/data/map');
const {getUserLevelInfo} = require('../../login/lib/dao');

//向用户发送最新的金币数量
exports.pushMsgUserCoins = async function (uid, sid) {
    debug("uid = %s  sid = %s  向用户发送最新的金币数量", uid, sid);
    //获取当前的升级信息
    let {coins, last_update_coins, depth_level, quality_level, spreed_level} = await getUpgradeData.call(this, uid);

    //计算当前的金币数
    let nowDate = new Date().valueOf();
    //当前采集收入速度
    let incomeRate = exports.getIncomeRate.call(this, depth_level, quality_level, spreed_level)
    //采集加速中
    if (await isIncomeRate.call(this, uid)) {
        incomeRate = incomeRate.times(INCOME_SPEED_UP_SCALE);
    }
    //上次更新金币距离现在的金币数
    let increaseCoins = incomeRate.times(parseInt((nowDate - last_update_coins) / 1000));
    //当前金币数
    let nowCoins = coins.plus(increaseCoins);
    //设置内存中upgrade的当前金币数量并修改上次更新时间
    await setUpgradeCoins.call(this, uid, nowCoins, nowDate);

    //金币和矿工名称消息，给客户端发送当前的金币和矿工名称
    this.app.get('channelService').pushMessageByUids(
        PushRoute.NOW_COINS, {coins: exports.formatCoins(nowCoins)}, [{uid, sid}]);
}

//深度升级
exports.depthUpgrade = async function (uid, sid) {
    debug("uid = %s  sid = %s  深度升级", uid, sid);
    //获取深度等级，品质等级，速度等级
    let {coins, depth_level, quality_level, spreed_level} = await getUpgradeData.call(this, uid);
    if (depth_level % 5 !== 0) {    //需要使用金币升级
        //计算当前用户升级所需金币
        let upgradeNeedCoins = exports.getDepthConsume(depth_level);
        //检测用户金币是否足够
        if (coins.comparedTo(upgradeNeedCoins) < 0) {
            debug("coins = %s  upgradeNeedCoins = %s  深度升级失败，用户金币不足", coins.toFormat(0), upgradeNeedCoins.toFormat());
            throw new Error(Msg.COINS_INSUFFICIENT);
        }

        //更新用户的金币数量并向用户返回
        await exports.pushMsgUserCoins.call(this, uid, sid);
        //扣除金币数量
        await reduceCoins.call(this, uid, upgradeNeedCoins);
        //更新用户的金币数量并向用户返回
        await exports.pushMsgUserCoins.call(this, uid, sid);
    }
    //深度升级
    await incrUpgradeDepth_level.call(this, uid);

    //depthGrade 当前深度等级
    let depthGrade = Number(depth_level) + 1;
    //depthNum 当前深度
    let depthNum = exports.getDepthNum(depthGrade);
    //incomeRate 采集收入速度，getIncomeRate返回的是BigNumber
    let incomeRate = exports.getIncomeRate.call(this, depthGrade, quality_level, spreed_level);
    //采集加速中
    if (await isIncomeRate.call(this, uid)) {
        incomeRate = incomeRate.times(INCOME_SPEED_UP_SCALE);
    }
    incomeRate = exports.formatCoins(incomeRate);
    //当前深度消耗
    let depthCoinsume = exports.getDepthConsume(depthGrade);
    depthCoinsume = exports.formatCoins(depthCoinsume);
    return {depthGrade, depthNum, incomeRate, depthCoinsume};
}

//品质升级
exports.qualityUpgrade = async function (uid, sid) {
    debug("uid = %s  sid = %s  品质升级", uid, sid);
    //获取深度等级，品质等级，速度等级
    let {coins, depth_level, quality_level, spreed_level} = await getUpgradeData.call(this, uid);
    if (quality_level % 5 !== 0) {    //需要使用金币升级
        //计算当前用户升级所需金币
        let upgradeNeedCoins = exports.getQualityConsume(quality_level);
        //检测用户金币是否足够
        if (coins.comparedTo(upgradeNeedCoins) < 0) {
            debug("coins = %s  upgradeNeedCoins = %s  品质升级失败，用户金币不足", coins.toFormat(0), upgradeNeedCoins.toFormat(0));
            throw new Error(Msg.COINS_INSUFFICIENT);
        }

        //更新用户的金币数量并向用户返回
        await exports.pushMsgUserCoins.call(this, uid, sid);
        //扣除金币数量
        await reduceCoins.call(this, uid, upgradeNeedCoins);
        //更新用户的金币数量并向用户返回
        await exports.pushMsgUserCoins.call(this, uid, sid);
    }
    //品质升级
    await incrUpgradeQuality_level.call(this, uid);

    //qualityGrade 当前品质等级
    let qualityGrade = Number(quality_level) + 1;
    //incomeRate 采集收入速度
    let incomeRate = exports.getIncomeRate.call(this, depth_level, qualityGrade, spreed_level);
    //采集加速中
    if (await isIncomeRate.call(this, uid)) {
        incomeRate = incomeRate.times(INCOME_SPEED_UP_SCALE);
    }
    incomeRate = exports.formatCoins(incomeRate);
    //当前品质消耗
    let qualityConsume = exports.getQualityConsume(qualityGrade);
    qualityConsume = exports.formatCoins(qualityConsume);
    return {qualityGrade, incomeRate, qualityConsume};
}

//速度升级
exports.spreedUpgrade = async function (uid, sid) {
    debug("uid = %s  sid = %s  速度升级", uid, sid);
    //获取深度等级，品质等级，速度等级
    let {coins, depth_level, quality_level, spreed_level} = await getUpgradeData.call(this, uid);
    if (spreed_level % 5 !== 0) {    //需要使用金币升级
        //计算当前用户升级所需金币
        let upgradeNeedCoins = exports.getSpreedCoinsConsume(spreed_level);
        //检测用户金币是否足够
        if (coins.comparedTo(upgradeNeedCoins) < 0) {
            debug("coins = %s  upgradeNeedCoins = %s  品质升级失败，用户金币不足", coins.toFormat(0), upgradeNeedCoins.toFormat(0));
            throw new Error(Msg.COINS_INSUFFICIENT);
        }

        //更新用户的金币数量并向用户返回
        await exports.pushMsgUserCoins.call(this, uid, sid);
        //扣除金币数量
        await reduceCoins.call(this, uid, upgradeNeedCoins);
        //更新用户的金币数量并向用户返回
        await exports.pushMsgUserCoins.call(this, uid, sid);
    }
    //品质升级
    await incrUpgradeSpreed_level.call(this, uid);

    //spreedLevelGrade 当前品质等级
    let spreedGrade = Number(spreed_level) + 1;
    //incomeRate 采集收入速度
    let incomeRate = exports.getIncomeRate.call(this, depth_level, quality_level, spreedGrade);
    //采集加速中
    if (await isIncomeRate.call(this, uid)) {
        incomeRate = incomeRate.times(INCOME_SPEED_UP_SCALE);
    }
    incomeRate = exports.formatCoins(incomeRate);
    //当前深度消耗
    let spreedCoinsConsume = exports.getSpreedCoinsConsume(spreedGrade);
    spreedCoinsConsume = exports.formatCoins(spreedCoinsConsume);
    return {spreedGrade, incomeRate, spreedCoinsConsume};
}

//返回当前采集收入速度算法
exports.getIncomeRate = function (depth_level, quality_level, spreed_level) {
    debug("depth_level = %s  quality_level = %s  spreed_level = %s 返回当前采集收入速度算法", depth_level, quality_level, spreed_level);
    let depthLevel = new BigNumber(depth_level);
    let qualityLevel = new BigNumber(quality_level);
    let spreedLevel = new BigNumber(spreed_level);

    let deepCoins = depthLevel.times(DepthConsts.quicken).times(DepthConsts.init);    //升级加速=级别*金币速度*初始金币数量
    let qualityCoins = qualityLevel.times(QualityConsts.quicken).times(QualityConsts.init);
    let spreedCoins = spreedLevel.times(SpreedConsts.quicken).times(SpreedConsts.init);
    let incomeRate = deepCoins.plus(qualityCoins).plus(spreedCoins);
    return incomeRate;
}

//当前深度
exports.getDepthNum = function (depth_level) {
    debug("depth_level = %s 计算当前深度", depth_level);
    return depth_level * DepthConsts.increaseLen;
}

//当前深度消耗
exports.getDepthConsume = function (depth_level) {
    debug("depth_level = %s 计算当前深度消耗", depth_level);
    depth_level = new BigNumber(depth_level);
    let prevConsume = new BigNumber(DepthConsts.init);    //上级所需经验
    for (let i = 1; i <= depth_level; i++) {
        prevConsume = prevConsume.times(DepthConsts[parseInt(i / 10)].scale).times(i);    //当前经验 = 上级所需经验 * 相应倍数 * 等级
    }
    return prevConsume;
}

//当前品质消耗
exports.getQualityConsume = function (quality_level) {
    debug("quality_level = %s 计算当前品质消耗", quality_level);
    quality_level = new BigNumber(quality_level);
    let prevConsume = new BigNumber(QualityConsts.init);    //上级所需经验
    for (let i = 1; i <= quality_level; i++) {
        prevConsume = prevConsume.times(QualityConsts[parseInt(i / 10)].scale).times(i);    //当前经验 = 上级所需经验 * 相应倍数 * 等级
    }
    return prevConsume;
}

//当前速度消耗
exports.getSpreedCoinsConsume = function (spreed_level) {
    debug("spreed_level = %s 计算当前速度消耗", spreed_level);
    spreed_level = new BigNumber(spreed_level);
    let prevConsume = new BigNumber(SpreedConsts.init);    //上级所需经验
    for (let i = 1; i <= spreed_level; i++) {
        prevConsume = prevConsume.times(SpreedConsts[parseInt(i / 10)].scale).times(i);    //当前经验 = 上级所需经验 * 相应倍数 * 等级
    }
    return prevConsume;
}

//金币增加
exports.addCoins = async function (session, uid, coins) {
    debug("uid = %s  coins = %s 金币增加", uid, coins);
    return await addCoinsApi.call(this, session, uid, coins);
}

//格式化用户金币
exports.formatCoins = function (coins) {
    debug("coins = %s  开始格式化用户金币", coins);
    //预格式化用户金币，转为字符串，并去除小数点
    coins = coins.toFormat(0);
    coins = "" + coins.replace(/[,]/ig, "");
    let coinsUnit = ["", "K", "M", "G", "T",
        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
        "aa", "ab", "ac", "ad", "ae", "af", "ag", "ah", "ai", "aj", "ak", "al", "am", "an", "ao", "ap", "aq", "ar", "as", "at", "au", "av", "aw", "ax", "ay", "az",
        "ba", "bb", "bc", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bk", "bl", "bm", "bn", "bo", "bp", "bq", "br", "bs", "bt", "bu", "bv", "bw", "bx", "by", "bz",
        "ca", "cb", "cc", "cd", "ce", "cf", "cg", "ch", "ci", "cj", "ck", "cl", "cm", "cn", "co", "cp", "cq", "cr", "cs", "ct", "cu", "cv", "cw", "cx", "cy", "cz",
        "da", "db", "dc", "dd", "de", "df", "dg", "dh", "di", "dj", "dk", "dl", "dm", "dn", "do", "dp", "dq", "dr", "ds", "dt", "du", "dv", "dw", "dx", "dy", "dz"
    ];
    for (let i = 0; i < coins.length; i++) {
        if (coins.length < 4 + 3 * i) {
            let coinsFormat = coins.substr(0, coins.length - 3 * i) + coinsUnit[i];
            return coinsFormat;
        }
    }
}

//获取离线金币和时长
exports.getLogOutCoins = async function (uid, last_login_time, last_logout_time) {
    console.warn("uid = %s  last_login_time = %s  last_logout_time = %s  获取离线金币数量", uid, last_login_time, last_logout_time);
    //获取当前的升级信息
    // let {depth_level, quality_level, spreed_level, last_update_coins} = await getUpgradeData.call(this, uid);

    //用户离线时长
    let logOutInterval = parseInt((last_login_time - last_logout_time) / 1000);
    //有效离线奖励时长
    logOutInterval = logOutInterval > 60 * 60 * 12 ? 60 * 60 * 12 : logOutInterval;
    if (logOutInterval < MIN_LOGOUT_INTERVAL) {
        console.warn("uid = %s  logOutInterval = %s  获取离线金币数量失败，离线时长过小", uid, logOutInterval);
        //重置更新时间为上线时间
        await setLastUpdateCoins.call(this, uid, last_login_time);
        return {logOutCoins: 0, logOutInterval: 0}
    }
    let levelInfo = await getUserLevelInfo.call(this, uid)
    if (levelInfo) {
        //秒产金量
        let incomeRate = await exports.getPerProduct.call(this, levelInfo, uid);
        //离线金币数量
        let logOutCoins = BigNumber(incomeRate).times(logOutInterval);
        //存储离线金币到缓存中
        await saveLogoutCoins.call(this, uid, logOutCoins);

        logOutCoins = exports.formatCoins(logOutCoins);
        console.warn("loginOutCoins = %s  loginInterval = %s  获取离线金币数量", logOutCoins, logOutInterval);
        return {logOutCoins, logOutInterval};
    } else {
        return {logOutCoins: 0, logOutInterval: 0}
    }
}

//cron定时更新用户金币数量
exports.cronSyncCoins = async function (uid, sid, last_login_time, last_logout_time) {
    debug("uid = %s  sid = %s  last_login_time = %s  last_logout_time = %s  cron更新用户金币数量", uid, sid, last_login_time, last_logout_time);
    //验证用户刚上线，并且未计算离线奖励，上次更新时间小于等于离线时间，并且上次更新时间不等于登陆时间
    let {last_update_coins} = await getUpgradeData.call(this, uid);
    if (last_update_coins <= last_logout_time && last_update_coins != last_login_time) {
        debug("last_update_coins = %s  last_logout_time = %s  last_login_time = %s  更新用户金币时未领取离线奖励退出", last_update_coins, last_logout_time, last_login_time);
        return;
    }
    //更新用户的金币数量并向用户返回
    await exports.pushMsgUserCoins.call(this, uid, sid);
}

//根据倍数领取离线金币数量
exports.getLogOutCoinsByMultiple = async function (session, uid, multiple = 1) {
    debug("uid = %s  multiple = %s  根据倍数领取离线金币数量", uid, multiple);
    //用户上线时间
    // let last_login_time = await getLastLoginTime.call(this, uid);
    //获取当前金币数
    // let {coins} = await getUpgradeData.call(this, uid);
    //获取离线金币数量
    let logOutCoins = await getLogoutCoins.call(this, uid);
    logOutCoins = logOutCoins.times(multiple);
    await addCoinsApi.call(this, session, uid, logOutCoins);
    //总金币数
    // let nowCoins = coins.plus(logOutCoins);
    //设置内存中upgrade的当前金币数量并修改上次更新时间
    // await setUpgradeCoins.call(this, uid, nowCoins, last_login_time);
}

//获取升级相关数据
exports.getUpgradeDate = async function (uid) {
    debug("uid = %s  获取升级相关数据", uid);
    //获取当前的升级信息
    let upgrade = await getUpgradeData.call(this, uid);
    if (!upgrade) return null;
    let {coins, depth_level, quality_level, spreed_level} = upgrade;
    //当前采集收入速度
    let incomeRateNum = exports.getIncomeRate.call(this, depth_level, quality_level, spreed_level);
    //采集加速中
    if (await isIncomeRate.call(this, uid)) {
        incomeRateNum = incomeRateNum.times(INCOME_SPEED_UP_SCALE);
    }
    let incomeRate = exports.formatCoins(incomeRateNum);

    return {coins, depth_level, quality_level, spreed_level, incomeRate, incomeRateNum};
}

//采集收入加速
exports.addIncomeRate = async function (uid, frontendId, time) {
    debug("uid = %s  time = %s  采集收入加速", uid, time);
    //设置采集收入加速
    await setIncomeRate.call(this, uid, time);
    //获取当前的升级信息
    let {depth_level, quality_level, spreed_level} = await getUpgradeData.call(this, uid);
    //当前采集收入速度
    let incomeRate = exports.getIncomeRate(depth_level, quality_level, spreed_level)
    //加速后的采集收入速度
    let speedUpIncomeRate = incomeRate.times(INCOME_SPEED_UP_SCALE);
    //向客户端发送采集收入开始加速消息
    onIncomeRateAdd.call(this, {incomeRate: exports.formatCoins(speedUpIncomeRate)}, uid, frontendId, function () {
    });
}

//采集收入结束事件，由redis调用
exports.incomeSpeedUpEnd = function (uid, frontendId) {
    const sub = redis.createClient(this.app.get('redisConfig'));
    const expired_subKey = '__keyevent@' + this.app.get('redis').options.db + '__:expired'
    //监听超时事件
    sub.subscribe(expired_subKey, () => {
        sub.on('message', async (chan, key) => {
            if (key.indexOf(ADD_INCOMERATE_PREFIX) !== 0) return;
            //获取uid
            let uid = key.slice(ADD_INCOMERATE_PREFIX.length);
            //获取frontendId
            let connectorServers = this.app.getServersByType('connector');
            let connectorServersId = dispatcher.dispatch(uid, connectorServers).id;
            debug("uid = %s 采集收入加速", uid);

            //获取当前的升级信息
            let {depth_level, quality_level, spreed_level} = await getUpgradeData.call(this, uid);
            //当前采集收入速度
            let incomeRate = exports.getIncomeRate(depth_level, quality_level, spreed_level)

            //向客户端发送采集收入加速结束消息
            onIncomeRateEnd.call(this, {incomeRate: exports.formatCoins(incomeRate)}, uid, connectorServersId, function () {
            });
        });
    });
}

//获取相应倍数的每小时产量
exports.getScaleIncomeRateByHours = async function (uid, scaleAry) {
    //获取当前的升级信息
    let {depth_level, quality_level, spreed_level} = await getUpgradeData.call(this, uid);
    //当前采集收入速度
    let incomeRate = exports.getIncomeRate.call(this, depth_level, quality_level, spreed_level);
    return scaleAry.map(scale => {
        let coins = incomeRate.times(60 * 60 * scale);
        //供客户端读取的金币数量
        let coins_str = exports.formatCoins(coins);
        //供数据库记录的金币数量
        coins = coins.toFormat(0);
        coins = "" + coins.replace(/[,]/ig, "");
        let coins_num = coins;
        return {coins_num, coins_str}
    });
}

/**
 * 获取矿层升级所需的信息
 * @param floor
 * @param count
 * @returns {Promise<void>}
 */
exports.getOpenInfoAction = async function (floor, count) {
    let self = this;
    let resArr = [];
    for (let i = 0; i < count; i++) {
        let floorName = utils.getFloorName(floor);
        let mapInfo = mapJson[floorName]; //要解锁的矿层信息
        let cost = utils.getOpenFloorMoney(mapInfo, floor);
        resArr.push({
            floor: floor,
            coins: exports.formatCoins(new BigNumber(cost))
        });
        floor += 1;
    }
    return resArr;
}

/**
 * 获取当前用户的矿层信息
 * @param uid
 * @returns {Promise<void>}
 */
exports.getUserFloorAction = async function (uid) {
    let self = this;
    let userInfo = await self.app.userRemote(uid, 'get', [uid, 'user']);
    if (!userInfo) {
        throw new Error(Msg.USER_NOT_FOUND);
    }
    let data = await getLevelInfo.call(self, uid);
    let productPerSec = 0; //每秒产金币量
    let level_info = [];
    if (data && data.length > 0) {
        for (let i = 0; i < data.length; i++) {
            productPerSec += data[i]['product'];
            level_info.push({
                'level_num': data[i]['level_num'],
                'floor_level': data[i]['floor_level'],
                'level_max': data[i]['level_max'],
                'uid': data[i]['uid'],
                'product': exports.formatCoins(new BigNumber(data[i]['product'])),
                'upgrade_need': exports.formatCoins(new BigNumber(data[i]['upgrade_need'])),
            });
        }
    }
    let double = await doubleRewardExists.call(self, uid);
    if (double) {
        productPerSec *= 2;
    }
    return {
        coins: exports.formatCoins(new BigNumber(userInfo.coins)),
        per_sec_product: exports.formatCoins(new BigNumber(productPerSec)),
        per_sec_offline: exports.formatCoins(new BigNumber(productPerSec / 10)),
        level_info: level_info
    };
}

/**
 * 解锁矿层
 * @param uid
 * @param floor
 * @returns {Promise<void>}
 */
exports.openFloorAction = async function (session, uid, floor) {
    let self = this;
    let userInfo = await self.app.userRemote(uid, 'get', [uid, 'user']);
    if (!userInfo) {
        throw new Error(Msg.USER_NOT_FOUND);
    }

    let level = userInfo.levelInfo ? userInfo.levelInfo : await getLevelInfo.call(self, uid);
    if (floor < 1) {
        throw new Error(Msg.FLOOR_ERR);
    }
    let floorName = utils.getFloorName(floor);
    let mapInfo = mapJson[floorName]; //要解锁的矿层信息
    if (!mapInfo) {
        throw new Error(Msg.MAP_UNDEFINED);
    }
    if (level.length == 0) {
        //空地图必须解锁第一层
        if (floor != 1) {
            throw new Error(Msg.FLOOR_ERR);
        }
        let current = new BigNumber(userInfo.coins);
        let cost = new BigNumber(mapInfo.open);
        //金币不足
        if (current.comparedTo(cost) == -1) {
            throw new Error(Msg.COINS_INSUFFICIENT);
        }
        //扣除开矿层需要的金币
        userInfo.coins -= mapInfo.open;
        if (userInfo.coins < 0) {
            userInfo.coins = 0;
        }
        //扣除开矿层需要的金币
        self.app.rpc.login.loginRemote.reduceCoins(session, uid, session.frontendId, mapInfo.open, function (err, msg) {
        })
        level = [];
        level[0] = {
            uid: userInfo.uid,
            level_num: floor,
            product: mapInfo.product,
            floor_level: 1,
            upgrade_need: mapInfo.upgrade_need,
            level_max: mapInfo.lvmax
        };
    } else {
        //获取要解锁的矿层信息
        let cost = new BigNumber(utils.getOpenFloorMoney(mapInfo, floor));
        let initProduct = utils.getOpenFloorInitProduct(mapInfo, floor);
        let initUpgrade = utils.getUpgradeInitCoins(mapInfo, floor);
        let current = new BigNumber(userInfo.coins);
        //金币不足
        if (current.comparedTo(cost) == -1) {
            throw new Error(Msg.COINS_INSUFFICIENT);
        }
        //扣除开矿层需要的金币
        self.app.rpc.login.loginRemote.reduceCoins(session, uid, session.frontendId, mapInfo.open, function (err, msg) {
        })
        level[floor - 1] = {
            uid: userInfo.uid,
            level_num: floor,
            product: initProduct,
            floor_level: 1,
            upgrade_need: initUpgrade,
            level_max: mapInfo.lvmax
        };
    }

    //秒产金量提升并推送
    let product = await exports.getPerProduct.call(self, level, uid);
    pushUtils.onProductChange(self.app, {
        product: exports.formatCoins(new BigNumber(product))
    }, uid, session.frontendId);

    //同步内存
    await this.app.userRemote(uid, "set", [uid, "user.levelInfo", level]);
    await this.app.userRemote(uid, "set", [uid, "user.current_level", floor]);

    return {
        uid: userInfo.uid,
        level_num: floor,
        product: exports.formatCoins(new BigNumber(level[floor - 1]['product'])),
        floor_level: 1,
        upgrade_need: exports.formatCoins(new BigNumber(level[floor - 1]['upgrade_need'])),
        level_max: mapInfo.lvmax
    };
}

/**
 * 升级矿层
 * @param session
 * @param uid
 * @param floor
 * @returns {Promise<void>}
 */
exports.upgradeFloorAction = async function (session, uid, floor) {
    let self = this;
    let userInfo = await self.app.userRemote(uid, 'get', [uid, 'user']);
    if (!userInfo) {
        throw new Error(Msg.USER_NOT_FOUND);
    }
    let level = userInfo.levelInfo ? userInfo.levelInfo : await getLevelInfo.call(self, uid);
    if (floor < 1) {
        throw new Error(Msg.FLOOR_ERR);
    }
    let floorName = utils.getFloorName(floor);
    let mapInfo = mapJson[floorName]; //要升级的矿层信息
    if (!mapInfo) {
        throw new Error(Msg.MAP_UNDEFINED);
    }
    //判断地图信息中是否有要升级的地图
    if (level.length < (floor - 1)) {
        throw new Error(Msg.FLOOR_NOT_OPEN);
    }
    //当前的楼层信息
    let currentLevel = level[floor - 1];
    //已经满级了
    if (currentLevel['floor_level'] == mapInfo['lvmax']) {
        throw new Error(Msg.LEVEL_MAX_ERR);
    }
    //每隔5层通过视频升级
    if ((parseInt(currentLevel['floor_level']) + 1) % 5 != 0) {
        //金币升级
        //获取升级所需的金币
        let coins = new BigNumber(currentLevel['upgrade_need']);
        let current = new BigNumber(userInfo.coins);
        //用户余额不足
        if (current.comparedTo(coins) == -1) {
            throw new Error(Msg.COINS_INSUFFICIENT);
        }
        //扣除开矿层需要的金币
        await reduceCoinsApi.call(self, session, uid, coins);
    }
    //等级上升
    currentLevel['floor_level'] += 1;
    //产金值上升
    currentLevel['product'] = utils.getLevelProductRepair(mapInfo, currentLevel['floor_level'], currentLevel['product']);
    //升级所需金币变更
    currentLevel['upgrade_need'] = utils.getUpgradeMoney(mapInfo, currentLevel['floor_level'], currentLevel['upgrade_need']);
    level[floor - 1] = currentLevel;

    //秒产金量提升并推送
    let product = await exports.getPerProduct.call(self, level, uid);
    pushUtils.onProductChange(self.app, {
        product: exports.formatCoins(new BigNumber(product))
    }, uid, session.frontendId);

    //同步内存
    await self.app.userRemote(uid, "set", [uid, "user.levelInfo", level]);

    return {
        level_num: currentLevel['level_num'],
        floor_level: currentLevel['floor_level'],
        upgrade_need: exports.formatCoins(new BigNumber(currentLevel['upgrade_need'])),
        level_max: currentLevel['level_max'],
        product: exports.formatCoins(new BigNumber(currentLevel['product'])),
        uid: currentLevel['uid']
    };
}

/**
 * 获取最新的秒产金量
 * @param levelInfo
 * @param uid
 * @returns {Promise<void>}
 */
exports.getPerProduct = async function (levelInfo, uid) {
    let self = this;
    let productPerSec = new BigNumber(0);
    for (let i = 0; i < levelInfo.length; i++) {
        productPerSec = productPerSec.plus(new BigNumber(levelInfo[i]['product']));
    }
    let double = await doubleRewardExists.call(self, uid);
    if (double) {
        productPerSec = productPerSec.times(2);
    }
    return productPerSec;
}

/**
 * 判断当前是否在双倍奖励中
 * @param uid
 * @returns {Promise<void>}
 */
exports.isInDoubleAct = async function (uid) {
    let self = this;
    return await doubleRewardExists.call(self, uid);
}

/**
 * 开启双倍奖励
 * @param uid
 * @returns {Promise<void>}
 */
exports.openDoubleRewardAction = async function (uid, frontendId, ttl) {
    let self = this;
    await openDouble.call(self, uid, ttl);
    let userInfo = await self.app.userRemote(uid, 'get', [uid, 'user']);
    if (userInfo && userInfo.levelInfo) {
        //秒产金量提升并推送
        let product = await exports.getPerProduct.call(self, userInfo.levelInfo, uid);
        pushUtils.onProductChange(self.app, {
            product: exports.formatCoins(new BigNumber(product))
        }, uid, frontendId);
        pushUtils.onDoubleRewardOpen(self.app, {
            product: exports.formatCoins(new BigNumber(product))
        }, uid, frontendId);
    }
}